package edu.uw.cs.cse461.sp12.OS;

import java.io.IOException;
import java.net.Socket;
import java.util.Properties;

import org.json.JSONException;
import org.json.JSONObject;

import edu.uw.cs.cse461.sp12.util.Log;
import edu.uw.cs.cse461.sp12.util.TCPMessageHandler;

/**
 * Implements a Socket to use in sending remote RPC invocations.  (It must engage
 * in the RPC handshake before sending the invocation request.)
 * @author zahorjan
 *
 */
public class RPCCallerSocket extends Socket {
	// This variable is part of the android Log.x idiom, as in Log.v(TAG, "some debugging log message")
	// You can use Log.x in console apps as well.
	private static final String TAG = "RPCCallerSocket";
	
	private String mRemoteHost;
	private TCPMessageHandler msghandler;
	
	/**
	 * Create a socket for sending RPC invocations, connecting it to the specified remote ip and port.
	 * @param hostname In Project 4, it's intended to be the string name of the remote system.  In Project 3, it's not terribly meaningful - repeat the ip.
	 * @param ip  Remote system IP address.
	 * @param port Remote RPC service's port.
	 * @throws IOException
	 */
	public RPCCallerSocket(String hostname, String ip, String port) throws IOException {
		super(ip, Integer.parseInt(port));
		mRemoteHost = hostname;

		// An rpc timeout value is specified in the config file.  You should use that one, not this literal.
		int rpcTimeout = 5000;  
		this.setSoTimeout(rpcTimeout);
		Properties p = OS.config();
		if (p.getProperty("rpc.timeout") != null) {
			this.setSoTimeout(Integer.parseInt(p.getProperty("rpc.timeout")));
		}
		
		
	JSONObject initial = new JSONObject();
	try {
		initial.put("id",1); //TODO: should this not be hardcoded?
		initial.put("host", hostname);
		initial.put("action", "connect");
		initial.put("type", "control");
		msghandler = new TCPMessageHandler(this);
		msghandler.sendMessage(initial);
		JSONObject returnVal = msghandler.readMessageAsJSONObject();
		if (returnVal.getString("type").equals("OK")) {
			return;
		} else {
			throw new IOException("Server returned error message");
		}
		
	} catch (JSONException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	}
	
	/**
	 * Close this socket.
	 */
	@Override
	public void close() {
		//TODO: implement
		try {
			super.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Returns the name of the remote host to which this socket is connected (as specified in the constructor call).
	 * Useful in Project 4.
	 */
	public String remotehost() {
		return mRemoteHost;
	}

	/**
	 * Causes a remote call to the service/method names by the arguments.
	 * @param service Name of the remote service (or application)
	 * @param method Method of that service to invoke
	 * @param userRequest Call arguments
	 * @return
	 */
	public JSONObject invoke(String service, String method, JSONObject userRequest) {
		//TODO: implement
		JSONObject returnVal = null;
		JSONObject toSend = new JSONObject();
		try {
			toSend.put("id", 4);
			toSend.put("app", service);
			toSend.put("host", remotehost());
			toSend.put("type", "invoke");
			toSend.put("args", userRequest);
			toSend.put("method", method);
			msghandler.sendMessage(toSend);
			returnVal = msghandler.readMessageAsJSONObject();
			returnVal = returnVal.getJSONObject("value");
			
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		return returnVal; //TODO: handle cases when the returned value is an error
	}
	
}
